use std::ops::{Deref, DerefMut};

// 实现一个智能指针，必须实现Deref特质
struct MyBox<T>(T);

impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}

// 重载deref方法
impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

// 重载deref_mut方法，前提条件已经实现Deref
impl<T> DerefMut for MyBox<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

struct CustomSmartPointer {
    data: String,
}

impl Drop for CustomSmartPointer {
    fn drop(&mut self) {
        println!("droping CustomSmartPointer with data {}", self.data)
    }
}

fn main() {
    // Deref
    // 解引用操作符号和常规引用
    let x = 5;
    let y = &x;

    assert_eq!(5, x);
    // 下面代码编译不通过，提示i32和&i32不能比较
    // assert_eq!(5, y);
    // 使用解引用符可以比较，解引用符直接拿到指针，直接得到i32类型指针
    assert_eq!(5, *y);

    let x = 5;
    let y = MyBox::new(5);

    assert_eq!(5, x);
    // 下面代码编译不通过，提示i32和MyBox<i32>不能比较
    // assert_eq!(5, y)
    // 使用解引用符可以比较，因为MyBox结构体已经实现了deref方法，可以使用*解引用获取相应的类型引用，*帮我们自动调用了deref方法，例如：*(y.deref())
    assert_eq!(5, *y);

    let mut b = MyBox::new(5);
    *b += 5;
    println!("b + 5 = {}", *b);

    // 解引用强制转换
    /*
     疑问：hello函数传入是&MyBox类型，没有提示编译错误，函数明明要求&str
     解答：先抛结论，因为rust内部解引用强制转换，帮我们做了一层层的解引用
     说明:
        1.首先赋值、函数或方法，在使用一个引用类型时会进行强制转换
        2.转换逻辑，逐曾进行，遇到与预期符合的类型停止，如果遇不到就直到不能转为止
     例如: &MyBox => &String => &str
    */
    // 实例：赋值
    let name = String::from("WuHan");
    let name: &str = &name;
    hello(name);

    // 示例：函数
    let name = MyBox::new(String::from("Rust"));
    hello(&name);

    // Drop
    // 以下这个执行后，会在main函数执行完，rust自动调用类型身上的drop方法，来做清理工作
    let b = CustomSmartPointer {
        data: String::from("my"),
    };
    println!("my stuff");
    let c = CustomSmartPointer {
        data: String::from("other"),
    };
    println!("other stuff");

    // 提问：如果想在main函数内部就调用drop方法，这是可以的嘛？
    // 解答：是可以的，但不是直接调用类型本身的drop方法，因为类型本身的drop是由rust在作用域结束后去自动调用的；如果主动调用会和rust冲突，带来不必要的问题；这里rust提供了一个drop函数，预导入的函数，可以满足以上需求

    let b = CustomSmartPointer {
        data: String::from("my"),
    };
    println!("my stuff");
    drop(b);
    let c = CustomSmartPointer {
        data: String::from("other"),
    };
    println!("other stuff");
}

fn hello(name: &str) {
    println!("hello {}", name)
}
